package br.ufal.questione.dao.hibernate;
import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Example;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.springframework.beans.factory.annotation.Autowired;

import br.ufal.questione.dao.DaoException;
import br.ufal.questione.dao.GenericDao;

public abstract class HibernateGenericDao<T, Pk extends Serializable> implements GenericDao<T, Pk> {

	@Autowired
	protected SessionFactory sessionFactory;

	@SuppressWarnings("unchecked")
	@Override
	public T consultar(Pk pk) {
		try {
			return (T) getSession().get(getClassEntidade(), pk);
		} catch (Exception e) {
			throw new DaoException(e);
		}
	}

	@Override
	public T inserir(T entity) {
		try {
			getSession().save(entity);
			return entity;
		} catch (Exception e) {
			throw new DaoException(e);
		}
	}
	
	public void editar(T entity) {
		try {
			getSession().update(entity);
		} catch (Exception e) {
			throw new DaoException(e);
		}
	}

	@Override
	public void excluir(T entity) {
		try {
			getSession().delete(entity);
		} catch (Exception e) {
			throw new DaoException(e);
		}
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<T> listar() {
		try {
			return getSession().createCriteria(getClassEntidade())
				.list();
		} catch (Exception e) {
			throw new DaoException(e);
		}
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<T> listar(String orderedField, boolean asc) {
		try {
			Criteria criteria = getSession().createCriteria(getClassEntidade());
			
			if (asc) {
				criteria.addOrder(Order.asc(orderedField));
			} else {
				criteria.addOrder(Order.desc(orderedField));
			}
			
			return criteria.list();
		} catch (Exception e) {
			throw new DaoException(e);
		}
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<T> listar(int offset, int maxResults) {
		try {
			return getSession().createCriteria(getClassEntidade())
				.setFirstResult(offset)
				.setMaxResults(maxResults)
				.list();
		} catch (Exception e) {
			throw new DaoException(e);
		}
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<T> listar(int offset, int maxResults, String orderedField, boolean asc) {
		try {
			Criteria criteria = getSession().createCriteria(getClassEntidade())
				.setFirstResult(offset)
				.setMaxResults(maxResults);
			
			if (asc) {
				criteria.addOrder(Order.asc(orderedField));
			} else {
				criteria.addOrder(Order.desc(orderedField));
			}
			
			return criteria.list();
		} catch (Exception e) {
			throw new DaoException(e);
		}
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<T> listar(T entity) {
		try {
			Example example = Example.create(entity).enableLike(MatchMode.ANYWHERE).excludeZeroes().ignoreCase();

			return getSession().createCriteria(getClassEntidade())
				.add(example)
				.list();
		} catch (Exception e) {
			throw new DaoException(e);
		}
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<T> listar(T entity, String orderedField, boolean asc) {
		try {
			Example example = Example.create(entity).enableLike(MatchMode.ANYWHERE).excludeZeroes().ignoreCase();
			
			Criteria criteria = getSession().createCriteria(getClassEntidade())
				.add(example);
		
			if (asc) {
				criteria.addOrder(Order.asc(orderedField));
			} else {
				criteria.addOrder(Order.desc(orderedField));
			}
			
			return criteria.list();
		} catch (Exception e) {
			throw new DaoException(e);
		}
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<T> listar(T entity, int offset, int maxResults) {
		try {
			Example example = Example.create(entity).enableLike(MatchMode.ANYWHERE).excludeZeroes().ignoreCase();

			return getSession().createCriteria(getClassEntidade())
				.add(example)
				.setFirstResult(offset)
				.setMaxResults(maxResults)
				.list();
		} catch (Exception e) {
			throw new DaoException(e);
		}
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<T> listar(T entity, int offset, int maxResults, String orderedField, boolean asc) {
		try {
			Example example = Example.create(entity).enableLike(MatchMode.ANYWHERE).excludeZeroes().ignoreCase();

			Criteria criteria = getSession().createCriteria(getClassEntidade())
				.add(example)
				.setFirstResult(offset)
				.setMaxResults(maxResults);
			
			if (asc) {
				criteria.addOrder(Order.asc(orderedField));
			} else {
				criteria.addOrder(Order.desc(orderedField));
			}
			
			return criteria.list();			
		} catch (Exception e) {
			throw new DaoException(e);
		}
	}

	@Override
	public Integer count() {
		try {
			return (Integer) getSession().createCriteria(getClassEntidade())
				.setProjection(Projections.rowCount())
				.uniqueResult();
		} catch (Exception e) {
			throw new DaoException(e);
		}
	}

	@Override
	public Integer count(T entity) {
		try {
			Example example = Example.create(entity).enableLike(MatchMode.ANYWHERE).excludeZeroes().ignoreCase();

			return (Integer) getSession().createCriteria(getClassEntidade())
				.setProjection(Projections.rowCount())
				.add(example)
				.uniqueResult();
		} catch (Exception e) {
			throw new DaoException(e);
		}
	}
	
	@SuppressWarnings("unchecked")
	protected Class<T> getClassEntidade() {
		return (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
	}
	
	protected Session getSession() {
		return this.sessionFactory.getCurrentSession();
	}

}
